﻿using System;
using System.Collections.Generic;
using System.Common;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;

namespace System.Controls.Converters
{
  public class EnumFlags2BoolConverter : IMultiValueConverter
  {
    private const int VALUE = 0;
    private const int SOURCE = 1;
    private const int NAME = 2;
    private const int MASK = 3;
    private static readonly object[] _emptyResult = { null, null, null };

    [DebuggerBrowsable( DebuggerBrowsableState.Never )]
    private string fPropertyName;

    [DebuggerBrowsable( DebuggerBrowsableState.Never )]
    private object fPropertySource;

    [DebuggerBrowsable( DebuggerBrowsableState.Never )]
    private PropertyValue fValue;

    private bool CheckParameters( object[] values )
    {
      if ( fPropertySource != null )
        return true;

      if ( fPropertySource == null && values[SOURCE] != null && values[SOURCE] != DependencyProperty.UnsetValue )
        fPropertySource = values[SOURCE];

      if ( string.IsNullOrEmpty( fPropertyName ) && values[NAME] != null && values[NAME] != DependencyProperty.UnsetValue )
        fPropertyName = values[NAME].ToString();

      if ( fPropertySource != null && !string.IsNullOrEmpty( fPropertyName ) )
        fValue = new PropertyValue( fPropertySource, fPropertyName );

      return true;
    }

    #region Implementation of IMultiValueConverter

    public object Convert( object[] values, Type targetType, object parameter, CultureInfo culture )
    {
      if ( values.Length < 3 || values[VALUE] == DependencyProperty.UnsetValue )
        return false;

      if ( parameter == null )
      {
        if ( values.Length > 3 )
          parameter = values[MASK];
      }

      if ( parameter == null )
        return false;


      var currentValue = 0;
      if ( !CheckParameters( values ) )
        return false;

      currentValue = System.Convert.ToInt32( values[VALUE] );

      var intMask = 0;
      if ( fValue != null && fValue.PropertyType.IsEnum )
      {
        intMask = (int) Enum.Parse( fValue.PropertyType, parameter.ToString(), true );
        return ( currentValue & intMask ) == intMask;
      }

      if ( int.TryParse( parameter.ToString(), out intMask ) )
        return ( currentValue & intMask ) == intMask;

      return false;
    }

    public object[] ConvertBack( object value, Type[] targetTypes, object parameter, CultureInfo culture )
    {
      if ( fValue == null || fPropertySource == null || value == null )
        return _emptyResult;

      var parameterString = parameter as string;
      if ( string.IsNullOrEmpty( parameterString ) )
        return _emptyResult;

      var boolValue = Converter.ConvertTo<bool>( value );
      var currentValue = Converter.ConvertTo<int>( fValue.Value );

      var intMask = 0;
      if ( fValue != null && fValue.PropertyType.IsEnum )
        intMask = (int) Enum.Parse( fValue.PropertyType, parameterString, true );
      else
      {
        if ( !int.TryParse( parameterString, out intMask ) )
          return _emptyResult;
      }

      if ( boolValue )
        currentValue |= intMask;
      else
        currentValue &= ~intMask;

      fValue.SetValue( fPropertySource, currentValue );

      return new[] { fValue.Value, fPropertySource, fPropertyName };
    }

    #endregion
  }
}
